/** ***************************************************************************
 * CoffeeRect.hpp
 * 2012 CoffeeParty Ltd.
 * Date Created: 2012-07-21                                  format YYYY-MM-DD
 * Revision Id: $Id$
 ******************************************************************************
 */

#ifndef _COFFEE_RECT_HPP_
#define _COFFEE_RECT_HPP_

#include "CoffeeCommons.hpp"

#ifdef __USE_LONG_HEADER_PATH
# include "CoffeeMate/glm/glm.hpp"
#else
# include "glm.hpp"
#endif

#define c_vector2d  glm::detail::tvec2
#define c_vector3d  glm::detail::tvec3
#define c_vector4d  glm::detail::tvec4

#define c_position  glm::detail::tvec2
#define c_dimension glm::detail::tvec2

template <class T>
class CoffeeRect
{
public:
    CoffeeRect() : UpperLeftCorner(0,0), LowerRightCorner(0,0) { }

    CoffeeRect(T x, T y, T x2, T y2)
    : UpperLeftCorner(x,y), LowerRightCorner(x2,y2) { }

    CoffeeRect(const c_position<T>& upperLeft, const c_position<T>& lowerRight)
    : UpperLeftCorner(upperLeft), LowerRightCorner(lowerRight) { }

    template<class U>
    CoffeeRect(const c_position<U>& upperLeft, const c_position<U>& lowerRight)
    : UpperLeftCorner(upperLeft), LowerRightCorner(lowerRight) { }

    template <class U>
    CoffeeRect(const c_position<T>& pos, const c_dimension<U>& size)
    : UpperLeftCorner(pos), LowerRightCorner(pos.x + size.x, pos.y + size.y) { }

    CoffeeRect<T> operator+(const c_position<T>& pos) const
    {
        CoffeeRect<T> ret(*this);
        return ret += pos;
    }

    CoffeeRect<T>& operator+=(const c_position<T>& pos)
    {
        UpperLeftCorner += pos;
        LowerRightCorner += pos;
        return *this;
    }

    CoffeeRect<T> operator-(const c_position<T>& pos) const
    {
        CoffeeRect<T> ret(*this);
        return ret -= pos;
    }

    CoffeeRect<T>& operator-=(const c_position<T>& pos)
    {
        UpperLeftCorner -= pos;
        LowerRightCorner -= pos;
        return *this;
    }

    bool operator==(const CoffeeRect<T>& other) const
    {
        return (UpperLeftCorner == other.UpperLeftCorner &&
            LowerRightCorner == other.LowerRightCorner);
    }

    bool operator!=(const CoffeeRect<T>& other) const
    {
        return (UpperLeftCorner != other.UpperLeftCorner ||
            LowerRightCorner != other.LowerRightCorner);
    }

    bool operator<(const CoffeeRect<T>& other) const
    {
        return getArea() < other.getArea();
    }

    T getArea() const
    {
        return getWidth() * getHeight();
    }

    bool isPointInside(const c_position<T>& pos) const
    {
        return (UpperLeftCorner.x <= pos.x && UpperLeftCorner.y <= pos.y
            && LowerRightCorner.x >= pos.x && LowerRightCorner.y >= pos.y);
    }

    bool isRectCollided(const CoffeeRect<T>& other) const
    {
        return (LowerRightCorner.y > other.UpperLeftCorner.y &&
            UpperLeftCorner.y < other.LowerRightCorner.y &&
            LowerRightCorner.x > other.UpperLeftCorner.x &&
            UpperLeftCorner.x < other.LowerRightCorner.x);
    }

    void clipAgainst(const CoffeeRect<T>& other)
    {
        if (other.LowerRightCorner.x < LowerRightCorner.x)
            LowerRightCorner.x = other.LowerRightCorner.x;
        if (other.LowerRightCorner.y < LowerRightCorner.y)
            LowerRightCorner.y = other.LowerRightCorner.y;

        if (other.UpperLeftCorner.x > UpperLeftCorner.x)
            UpperLeftCorner.x = other.UpperLeftCorner.x;
        if (other.UpperLeftCorner.y > UpperLeftCorner.y)
            UpperLeftCorner.y = other.UpperLeftCorner.y;

        if (UpperLeftCorner.y > LowerRightCorner.y)
            UpperLeftCorner.y = LowerRightCorner.y;
        if (UpperLeftCorner.x > LowerRightCorner.x)
            UpperLeftCorner.x = LowerRightCorner.x;
    }

    bool constrainTo(const CoffeeRect<T>& other)
    {
        if (other.getWidth() < getWidth() || other.getHeight() < getHeight())
            return false;

        T diff = other.LowerRightCorner.x - LowerRightCorner.x;
        if (diff < 0)
        {
            LowerRightCorner.x += diff;
            UpperLeftCorner.x  += diff;
        }

        diff = other.LowerRightCorner.y - LowerRightCorner.y;
        if (diff < 0)
        {
            LowerRightCorner.y += diff;
            UpperLeftCorner.y  += diff;
        }

        diff = UpperLeftCorner.x - other.UpperLeftCorner.x;
        if (diff < 0)
        {
            UpperLeftCorner.x  -= diff;
            LowerRightCorner.x -= diff;
        }

        diff = UpperLeftCorner.y - other.UpperLeftCorner.y;
        if (diff < 0)
        {
            UpperLeftCorner.y  -= diff;
            LowerRightCorner.y -= diff;
        }

        return true;
    }

    T getWidth() const
    {
        return LowerRightCorner.x - UpperLeftCorner.x;
    }

    T getHeight() const
    {
        return LowerRightCorner.y - UpperLeftCorner.y;
    }

    void repair()
    {
        if (LowerRightCorner.x < UpperLeftCorner.x)
        {
            T t = LowerRightCorner.x;
            LowerRightCorner.x = UpperLeftCorner.x;
            UpperLeftCorner.x = t;
        }

        if (LowerRightCorner.y < UpperLeftCorner.y)
        {
            T t = LowerRightCorner.y;
            LowerRightCorner.y = UpperLeftCorner.y;
            UpperLeftCorner.y = t;
        }
    }

    bool isValid() const
    {
        return ((LowerRightCorner.x >= UpperLeftCorner.x) &&
            (LowerRightCorner.y >= UpperLeftCorner.y));
    }

    c_position<T> getCenter() const
    {
        return c_position<T>(
                (UpperLeftCorner.x + LowerRightCorner.x) / 2,
                (UpperLeftCorner.y + LowerRightCorner.y) / 2);
    }

    c_dimension<T> getSize() const
    {
        return c_dimension<T>(getWidth(), getHeight());
    }

    void addInternalPoint(const c_position<T>& p)
    {
        addInternalPoint(p.X, p.Y);
    }

    void addInternalPoint(T x, T y)
    {
        if (x > LowerRightCorner.x)
            LowerRightCorner.x = x;
        if (y > LowerRightCorner.y)
            LowerRightCorner.y = y;

        if (x < UpperLeftCorner.x)
            UpperLeftCorner.x = x;
        if (y < UpperLeftCorner.y)
            UpperLeftCorner.y = y;
    }

    c_position<T> UpperLeftCorner;
    c_position<T> LowerRightCorner;
};

#ifndef c_rect
# define c_rect CoffeeRect
#endif

#endif /** _COFFEE_RECT_HPP_ */

